package cn.tedu.gateway.filters;

import cn.tedu.gateway.constant.UriWhiteListConstants;
import cn.tedu.gateway.util.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
import static cn.tedu.gateway.util.JWTUtil.validateRefreshToken;

@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    private UriWhiteListConstants uriWhiteListConstants;

    /*
     * ServerWebExchange的对象，代表的是前端发送来的http的上下文，
     * 包含请求和响应对象
     *
     * GatewayFilterChain,当作为网关运行时，可以用来添加过滤器链
     * */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //这是对传入 HTTP 请求的封装。它提供了访问请求头、查询参数、路径变量等的方法
        ServerHttpRequest request = exchange.getRequest();
        log.debug("request:{}", request);
        //这是对出站 HTTP 响应的封装。它允许你设置响应头、状态码、编码、体内容等
        ServerHttpResponse response = exchange.getResponse();

        //通过http请求的封装request，获取请求的地址并将这个地址转为String类型
        String uri = request.getURI().toString();
        log.debug("请求路径uri:{}", uri);
        //通过http请求的封装request，获取请求头，再通过getFirst()方法来匹配于 token 匹配的头部值 来获取token
        String visitToken = request.getHeaders().getFirst("Authorization"); //这个token包含'Bearer '
        String refreshToken = request.getHeaders().getFirst("RefreshKey");
        //判断该请求是否是 OPTION请求，如果是则调用之后的过滤器进行处理
        if ("option".equals(request.getMethod())) {
            return chain.filter(exchange);
        }
        //判断当前路径是否在白名单中
        if (uriWhiteListConstants.isInWhitelist(uri)) {
            //如果在则返回flag为true
            return chain.filter(exchange);
        }
        //判断这个请求所携带的访问token是否为空
        if (StringUtils.isEmpty(visitToken)) {
            //如果为空则响应状态 401 表示没有登录授权
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //response.setComplete() 方法通常用于异步非阻塞的Web框架中，用来标记一个HTTP响应已经完成并且可以关闭连接或释放资源
            return response.setComplete();
        }
        if (validateRefreshToken(refreshToken)) {
            //如果refreshToken过期，则响应状态 401 表示没有登录授权
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //response.setComplete() 方法通常用于异步非阻塞的Web框架中，用来标记一个HTTP响应已经完成并且可以关闭连接或释放资源
            return response.setComplete();
        }
        //token不为空才会走下面，所有保证subToken不会空指针
        String subToken = visitToken.substring(7);
        //使用JWTUtil工具类中的verify(),进行token校验，判断是否被恶意修改或错误
        String newVisitToken;
        try {
            newVisitToken = JWTUtil.verify(subToken, refreshToken);
        } catch (Exception e) {
            //与token为空同理
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        if (newVisitToken != null) {
            //不为空说明已经过期了，生成了新的token
            //将新生成的visitToken替换原来request.headers中的visitToken
            /*
            这行代码将一个名为token的头部添加到了HTTP响应中，并将它的值设为newToken。
            如果响应中已经存在一个名为token的头部，那么这个头部的值将被追加到现有值后面。
             */
            response.getHeaders().add("newToken", newVisitToken);
            /*
            * 这行代码设置了CORS响应头Access-Control-Expose-Headers
            * 指定了哪些自定义响应头可以被前端JavaScript访问。
            * 在这里，我们指定了newToken头部可以被暴露给前端。
            * */
            response.getHeaders().set("Access-Control-Expose-Headers", "NewToken");
            response.setStatusCode(HttpStatus.CREATED);
            //放行新请求
            return chain.filter(exchange);
        } else {
            //放行请求
            return chain.filter(exchange);
        }
    }

    /*
     * 实现Ordered接口的类，重写方法后
     * 返回的order值越小，优先级越高
     * 其中网关的底层通过一个实现了Ordered接口的一个类
     * 它的优先级值，即order值是int值的正最大值，优先级是最小的
     * */
    @Override
    public int getOrder() {
        return 1;
    }


}
